#include <stdlib.h>

#include "map.h"
#include "graph.h"

static void
node_dispose_set(void *data, void *extra)
{
	Graph_node *node = data;
	Disposer *vrtx_dspr = extra;

	if (vrtx_dspr)
		vrtx_dspr->dispose(node->vertex, vrtx_dspr->extra);

	set_dispose(&node->edges, NULL);
	free(data);
}

static void
node_dispose_map(void *data, void *extra)
{
	Graph_node *node = data;
	Disposer *vrtx_dspr = extra;

	if (vrtx_dspr)
		vrtx_dspr->dispose(node->vertex, vrtx_dspr->extra);

	map_dispose(&node->edges);
	free(data);
}

Nit_error
graph_init(Graph *graph, Disposer *vrtx_dspr, int map)
{
	graph->nd_dspr.dispose = map ? node_dispose_map : node_dispose_set;
	graph->nd_dspr.extra = vrtx_dspr;
        return set_init(&graph->nodes, &graph->nd_dspr, 0);
}

void
graph_dispose(Graph *graph)
{
	set_dispose(&graph->nodes, NULL);
}

Graph_node *
graph_node(Graph *graph, void *vertex)
{
	Graph_node *node = malloc(sizeof(*node));

	if (!node)
		return NULL;

        if (set_init(&node->edges, NULL, 0))
	        goto err_node;

	node->vertex = vertex;

	/* add ptr */
	if (set_add(&graph->nodes, sizeof(node), node))
	        goto err_set;

	return node;
err_set:
	set_dispose(&node->edges, NULL);
err_node:
	free(node);
	return NULL;
}

void *
graph_denode(Graph *graph, Graph_node *node, int ret_ver)
{
	void *vertex = node->vertex;

	set_remove(&graph->nodes, sizeof(node), (void **) &node);
	graph->nd_dspr.dispose(node, ret_ver ? graph->nd_dspr.extra : NULL);
        return ret_ver ? vertex : NULL;
}

Nit_error
graph_join(Graph_node *from, Graph_node *to,
	   uint32_t key_len, const void *key)
{
	if (key)
		return map_add(&from->edges, key_len, key, to);

	return set_add(&from->edges, sizeof(to), &to);
}

void
graph_disjoin(Graph_node *from, Graph_node *to,
	      uint32_t key_len, const void *key)
{
	void *val;

	if (key) {
		map_remove(&from->edges, key_len, key, &val);
	} else {
		set_remove(&from->edges, sizeof(to), (void **) &to);
	}
}

Graph_node *
graph_get(Graph_node *node, uint32_t key_len, const void *key)
{
	void *val;

	if (!map_get(&node->edges, key_len, key, &val))
		return NULL;

	return val;
}

int
graph_linked(Graph_node *from, Graph_node *to, Set_key *map_key)
{
	int bin;
	Set_entry *entry;

	if (!map_key)
		return set_contains(&from->edges, sizeof(to), &to);

	map_foreach (&from->edges, bin, entry)
		if (map_val(entry->dat, entry->key_len) == to) {
			map_key->dat = entry->dat;
			map_key->key_len = entry->key_len;
			return 1;
		}

	return 0;
}
